﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using CloudWars.Animation;

namespace CloudWars
{
    public abstract class Unit
    {
        float MOVE_SPEED = 3;

        public abstract string Title { get; }
        public abstract float MaxHP { get; }
        public abstract int MaxMove { get; }
        public abstract int AttackPower { get; }
        public abstract int AttackRange { get; }
        public abstract Texture2D UnitTexture { get; }
        public abstract SpriteAnimation MoveAnimation { get; }
        public abstract SpriteAnimation AttackAnimation { get; }
        
        public Direction Facing { get; set; }

        public string Name { get; set; }
        public float CurrentHP { get; set; }
        public Point GridLocation { get; set; }
        public bool IsSelected { get; set; }
        public Point RealLocation { get; set; }
        public Player Owner { get; set; }

        public bool HasAttacked { get; set; }
        public Unit AttackTarget { get; set; }
        public bool IsAttacking
        {
            get
            {
                return (AttackTarget != null);
            }
        }
        public bool HasMoved { get; set; }
        public bool HasActions
        {
            get
            {
                return !HasAttacked || !HasMoved;
            }
        }

        public bool IsMoving
        {
            get
            {
                Point endLocation = new Point(Game1.GRID_TILE_SIZE * GridLocation.X, Game1.GRID_TILE_SIZE * GridLocation.Y);
                return RealLocation != endLocation;
            }
        }

        private static SpriteAnimation deathAnimation;
        public SpriteAnimation DeathAnimation
        {
            get
            {
                return deathAnimation;
            }
        }

        public bool IsDying { get; set; }

        protected static Texture2D HpTexture;
        protected static SpriteAnimation SelectedAnimation { get; private set; }
        protected FloatingText CombatText { get; private set; }
        static SpriteFont FloatingTextFont;
        private static Texture2D HelmetTexture;

        public string HitPoints
        {
            get
            {
                return string.Format("{0}/{1}", CurrentHP, MaxHP);
            }
        }

        public Unit(Player owner, string name, int x, int y, OnUnitDeathDelegate onDeath)
        {
            Owner = owner;
            Name = name;
            GridLocation = new Point(x, y);
            IsSelected = false;
            CurrentHP = MaxHP;
            RealLocation = new Point(Game1.GRID_TILE_SIZE * GridLocation.X, Game1.GRID_TILE_SIZE * GridLocation.Y);
            CombatText = new FloatingText(FloatingTextFont);
            OnDeath += onDeath;
        }

        public static void LoadTextures(ContentManager contentManager)
        {
            HpTexture = contentManager.Load<Texture2D>("hpbar");
            HelmetTexture = contentManager.Load<Texture2D>("helmet");
            SelectedAnimation = new SpriteAnimation(contentManager.Load<Texture2D>("SelectedCircle"), 6);
            SelectedAnimation.Position = new Vector2(50, 50);
            SelectedAnimation.IsLooping = true;
            SelectedAnimation.FramesPerSecond = 20;

            deathAnimation = new SpriteAnimation(contentManager.Load<Texture2D>("explosion"), 25);
            deathAnimation.Position = new Vector2(50, 50);
            deathAnimation.IsLooping = false;
            deathAnimation.FramesPerSecond = 40;

            FloatingTextFont = contentManager.Load<SpriteFont>("FloatingCombatText");
        }

        public void TakeDamage(int damage)
        {
            CurrentHP -= damage;
            CombatText.ShowText(RealLocation,  damage.ToString(), Color.Pink);

            if (CurrentHP <= 0)
            {
                IsDying = true;
            }
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            if (!IsDying)
            {
                var unitPos = new Rectangle(RealLocation.X,
                                            RealLocation.Y,
                                            Game1.GRID_TILE_SIZE,
                                            Game1.GRID_TILE_SIZE);

                Point endLocation = new Point(Game1.GRID_TILE_SIZE * GridLocation.X, Game1.GRID_TILE_SIZE * GridLocation.Y);

                if (IsMoving)
                {
                    MoveAnimation.Position = new Vector2(RealLocation.X, RealLocation.Y);
                    MoveAnimation.Draw(spriteBatch, RealLocation.X > endLocation.X ? SpriteEffects.FlipHorizontally : SpriteEffects.None);
                }
                else if (IsAttacking)
                {
                    SpriteEffects attackDirectionEffect = SpriteEffects.None;
                    if (AttackTarget.GridLocation.X < GridLocation.X
                        || Facing == Direction.Left)
                    {
                        attackDirectionEffect = SpriteEffects.FlipHorizontally;
                    }

                    AttackAnimation.Position = new Vector2(RealLocation.X, RealLocation.Y);
                    AttackAnimation.Draw(spriteBatch, attackDirectionEffect);
                }
                else
                {
                    Color unitColor = Color.White;

                    if (!HasActions)
                    {
                        unitColor = Color.Gray;
                    }
                    else if (IsSelected)
                    {
                        SelectedAnimation.Position = new Vector2(RealLocation.X, RealLocation.Y);
                        SelectedAnimation.Draw(spriteBatch, SpriteEffects.None);
                    }

                    spriteBatch.Draw(UnitTexture, unitPos, UnitTexture.Bounds, unitColor, 0f, Vector2.One, Facing == Direction.Left ? SpriteEffects.FlipHorizontally : SpriteEffects.None, 0);
                }
                spriteBatch.Draw(HelmetTexture, unitPos, UnitTexture.Bounds, Owner.Color, 0f, Vector2.One, Facing == Direction.Left ? SpriteEffects.FlipHorizontally : SpriteEffects.None, 0);

                DrawHP(spriteBatch);
            }
            else
            {
                DeathAnimation.Position = new Vector2(RealLocation.X, RealLocation.Y);
                DeathAnimation.Draw(spriteBatch, SpriteEffects.None);
            }

            CombatText.Draw(spriteBatch);
        }

        private void DrawHP(SpriteBatch spriteBatch)
        {
            var maxHpPos = new Rectangle(RealLocation.X + 7,
                                      RealLocation.Y + 3,
                                      HpTexture.Width + 2,
                                      HpTexture.Height + 2);

            var curhpPos = new Rectangle(RealLocation.X + 8,
                                      RealLocation.Y + 4,
                                      (int)(HpTexture.Width * CurrentHP / MaxHP),
                                      HpTexture.Height);

            spriteBatch.Draw(HpTexture, maxHpPos, Color.Black);
            spriteBatch.Draw(HpTexture, curhpPos, Owner.Color);
        }

        public void Update(GameTime gameTime)
        {
            if (IsAttacking)
            {
                AttackAnimation.Update(gameTime);

                if (AttackAnimation.IsAnimationDone)
                {
                    AttackTarget = null;
                    AttackAnimation.Reset();
                }
            }

            CombatText.Update();

            if (IsSelected)
            {
                SelectedAnimation.Update(gameTime);
            }

            Point endLocation = new Point(Game1.GRID_TILE_SIZE * GridLocation.X, Game1.GRID_TILE_SIZE * GridLocation.Y);

            if (IsDying)
            {
                DeathAnimation.Update(gameTime);
            }

            if (RealLocation != endLocation)
            {
                MoveAnimation.Update(gameTime);

                int distance = endLocation.DistanceTo(RealLocation);

                if (distance > MOVE_SPEED)
                {
                    int dX = (int)(MOVE_SPEED * (endLocation.X - RealLocation.X)) / distance;
                    int dY = (int)(MOVE_SPEED * (endLocation.Y - RealLocation.Y)) / distance;

                    RealLocation = new Point(RealLocation.X + dX, RealLocation.Y + dY);
                }
                else
                {
                    RealLocation = endLocation;
                }
            }
        }

        public void WakeUnit()
        {
            HasMoved = false;
            HasAttacked = false;
        }

        public void Die()
        {
            OnDeath(this);
            IsDying = false;
            DeathAnimation.Reset();

            if (Owner.Units.Count == 0)
            {
                Owner.Defeat();
            }
        }

        public delegate void OnUnitDeathDelegate(Unit unit);
        public event OnUnitDeathDelegate OnDeath;
    }
}
